JavaScript环境


JavaScript环境概念

脚本运行在不同窗口的JavaScript环境 . 例如 , 应用中每个窗口都拥有属于自己的全局对象以及全局结构(Array , Object) .

多数浏览器惯用做法以及好的处理方式 , 如下:

  • 对象属性替换 , 库扩展或者简单脚本(如Prototype) , 不同窗口之间使用相似对象将不会受到影响 .
  • 单个窗口范围内程序错误(如构造对象前缺少new)或者bug不会影响到其他窗口 .
  • 其他窗口中恶意应用不能访问重要数据 .

当脚本访问其他环境中的对象或者函数 , JS引擎将提供临时环境处理 , 完成之后立即离开 .

NW.js环境

NW.js基于Chrome应用构建 . 因此NW.js在开始运行时 , 自动完成后台加载 . 当创建一个窗口时 , 同时创建一个JavaScript环境 .

NW.js中 , 默认情况下 , Node.js模块加载到后台运行环境 . 混合环境模式中 , 每个窗口或frame中都加载在环境中 . 参考独立环境模式混合环境模式 .

独立环境模式

除了浏览器环境 , NW.js默认在后台增加Node环境运行Node模块 . 这样NW.js同时拥有两个JavaScript环境: 浏览器环境 and Node环境 .

注 网页环境
实际上网页环境运行在独立的JavaScript环境 , 没有浏览器环境以及Node环境 . 页面环境不能访问网络 , Node.js或者NW.js环境APIs .

浏览器环境

加载脚本

浏览器环境中 , 脚本加载以及页面嵌入可以使用<script>标签或者jQuery的$.getScript()或者RequireJS .

全局对象

浏览器环境中 , 全局对象包括JS内建对象(如Date , Error , TypedArray)和Web API (如DOM API) .

创建新浏览器环境

每个窗口或frame有属于自己的环境 . 当创建新的frame或者窗口时 , 将得到一个新的浏览器环境 .

访问Node.js和NW.js的API

Node环境对象拷贝到浏览器环境中 , 这样运行在浏览器环境的脚本能够访问Node.js对象 :

  • nw -- 所有NW对象 NW.js APIs
  • global -- NW环境全局对象; 等同nw.global
  • require -- 加载Node.js模块的方法; 等同nw.require(), 但不支持通过require('nw.gui')加载NW.js模块.
  • process -- Node.js模块中process模块; 等同nw.process
  • Buffer -- Node.js模块中Buffer类

require()中相对路径解析

浏览器环境中相对路径解析参考主页面的路径 .

Node环境

加载脚本

Node运行环境加载脚本的方式:

全局对象

Node环境中运行的脚本能够使用JS内建对象 . 此外 , 还可以使用Node.js全局对象 , 如__dirname, process, Buffer等 .

Node环境不能使用Web APIs . 参考[Node环境访问浏览器以及NW.js的API](#access-browser-and-nwjs-api-in-node-context)查找使用的方法 .

Create New Node Context 创建新的Node环境

独立环境模式中 , 所有的Node环境包含所有Node模块 . 但可以使用以下方法创建新的Node环境:

Node环境访问浏览器API和NW.js的API

Node环境中 , 没有浏览器支持以及NW.js的APIs , 如alert() , document.* , nw.Clipboard等 . 访问浏览器APIs , 需要通过相应的对象 , 如window .

以下例子说明Node环境中如何访问 .

Node环境运行一下脚本(myscript.js):

// 浏览器环境需要传入`el`
exports.setText = function(el) {
    el.innerHTML = 'hello';
};

浏览器(index.html):

<div id="el"></div>
<script>
var myscript = require('./myscript');
// 将`el`元素传入Node函数
myscript.setText(document.getElementbyId('el'));
// "hello"将显示在`el`标签中
</script>
Node环境中的`window`对象指向后台中的DOM`window`对象 .

require()方法中相对路径解析

Node模块中的相对路径相对于当前模块的路径 . 与Node.js中相对路径解析方式相同 .

混合环境模式

混合环境在0.13版本中引入 . NW.js使用--mixed-context命令行参数运行 , 当浏览器环境创建 , 同时创建Node环境 .

混合环境模式中加载脚本

To enable Mixed context, add --mixed-context when starting NW.js or add it to chromium-args in Manifest file.

NW.js可以通过两种方式使用混合环境模式 , 一是使用--mixed-context命令行参数 , 二是在配置文件中添加chromium-args属性.

页面或Node.js中使用require()方式加载脚本运行在同样的环境中 .

全局对象

In Mixed context, you can use all browser and NW.js API in Node modules, and vice versa.

混合环境模式 , 可以使用浏览器以及Node模块中NW.js的API , 反之依然 .

package.json

{
    "name": "test-context",
    "main": "index.html",
    "chromium-args": "--mixed-context"
}

myscript.js

exports.createDate = function() {
    return new Date();
};

exports.showAlert = function() {
    alert("I'm running in Node module!");
};

index.html

<script>
var myscript = require('./myscript');

console.log(myscript.createDate() instanceof Date); // true
myscript.showAlert(); // I'm running in Node module!
</script>

混合环境模式和独立环境模式对比

独立环境模式的优势是不会出现类型检查问题 .

混合环境模式的缺点是不能轻易的分享变量 . 环境间分享变量 , 需要将变量放入其他环境能够访问的通用环境中 . 或者可以使用window.postMessage() API在环境之间发送和接收信息 .

多种环境工作

While differences of contexts are generally benefitial, sometimes they may constitute a problem in your (or some other person's) code, and a need for a workaround arises.

环境之间的差异通常是有利的 , 但有时可能会引起问题 .

例如 , 不同浏览器环境中 , 全局对象并不相同 , 环境之间的类型检查会出错 .

<iframe id="myframe" src="myframe.html"></iframe>
<script>
// `window`是当前浏览器环境的全局对象
// `myframe.contentWindow`是`<iframe>`环境中的全局对象
var currentContext = window;
var iframeContext = document.getElementById('myframe').contentWindow;

// 当前环境定义的`myfunc`
function myfunc() {

}

console.log(currentContext.Date === iframeContext.Date); // false
console.log(currentContext.Function === iframeContext.Function); // false
console.log(myfunc instanceof currentContext.Function); // true
console.log(myfunc instanceof iframeContext.Function); // false
console.log(myfunc.constructor === currentContext.Function); // true
console.log(myfunc.constructor === iframeContext.Function); // false
</script>

instanceOf问题

这类问题常发生在JavaScript中使用instanceof操作过程 . 参考MDN , Value instanceof Constructor操作测试对象是否在结构属性prototype的原型链中 . 然而 , 如果不同JavaScript环境通过Value , 而Value有自己父类结构 , 那么Value instanceof Constructor必然检查失败 .

例如 , 简单的检查Value instanceof Array , 在其他环境中能够通过 , 不能代表Value变量的值是Array中的值 .

obj.constructor问题

obj.constructor属性检查会引发问题 . 例如 , Value.constructor === Array被代替Value instanceof Array使用 .

obj.__proto__问题

obj.__proto__访问对象属性会引发问题 . 比较全局对象构造函数或者使用instanceof将会引起错误结果 .

第三方库问题

第三方库可能引发以上类型检查问题 . 引发的问题可能不易查处原因 , 一旦发生 , 可能是由于第三方库引发 . 可以提交问题报告或者自行解决 .

可靠的跨环境类型检查

当使用其他JavaScript环境中的值时 , 避免使用instanceof防止环境相关问题 .

可以使用Array.isArray方法检查值是否在数组中 , 这种方式比较可靠 .

可以使用环境全局对象检查Value类型 , 例如FunctionDate等 . 可以使用以下方式检查对象的真实类型 .

// 检查方法
Object.prototype.toString.apply(someValue) === "[object Function]"
// 检查日期
Object.prototype.toString.apply(someValue) === "[object Date]"

然而 , 没有现成的可以替代的方法 , 当面对问题是需要讨论之后进行修补 .

Node环境中可以使用nwglobal获取全局对象进行类型检查 .